// C++ STL
#include <string>
#include <functional>
using namespace std;

// owner
#include "LogMessage.hpp"

#ifndef LOGGER_HPP
#define LOGGER_HPP

namespace tulun
{
    class Logger
    {
    public:
        using OutputFun = std::function<void(const std::string &)>;
        using FlushFun = std::function<void(void)>;
        static void setOutput(OutputFun fun);
        static void setFlush(FlushFun fun);

    private:
        static OutputFun s_output_;
        static FlushFun s_flush_;

    private:
        tulun::LogMessage impl_;

    public:
        Logger(const tulun::LOG_LEVEL &level,
               const string &filename,
               const string &funcname,
               const int line);
        ~Logger();
        tulun::LogMessage &stream();

    private:
        static tulun::LOG_LEVEL s_level_;

    public:
        static tulun::LOG_LEVEL getLogLevel();
        static void setLogLevel(const tulun::LOG_LEVEL &level);
    };

#define LOG_TRACE                                                \
    if (tulun::Logger::getLogLevel() <= tulun::LOG_LEVEL::TRACE) \
    tulun::Logger(tulun::LOG_LEVEL::TRACE, __FILE__, __func__, __LINE__).stream()
#define LOG_DEBUG                                                \
    if (tulun::Logger::getLogLevel() <= tulun::LOG_LEVEL::DEBUG) \
    tulun::Logger(tulun::LOG_LEVEL::DEBUG, __FILE__, __func__, __LINE__).stream()
#define LOG_INFO                                                \
    if (tulun::Logger::getLogLevel() <= tulun::LOG_LEVEL::INFO) \
    tulun::Logger(tulun::LOG_LEVEL::INFO, __FILE__, __func__, __LINE__).stream()
#define LOG_WARN                                                \
    if (tulun::Logger::getLogLevel() <= tulun::LOG_LEVEL::WARN) \
    tulun::Logger(tulun::LOG_LEVEL::WARN, __FILE__, __func__, __LINE__).stream()
#define LOG_ERROR \
    tulun::Logger(tulun::LOG_LEVEL::ERROR, __FILE__, __func__, __LINE__).stream()
#define LOG_FATAL \
    tulun::Logger(tulun::LOG_LEVEL::FATAL, __FILE__, __func__, __LINE__).stream()
#define LOG_SYSERR \
    tulun::Logger(tulun::LOG_LEVEL::SYSERR, __FILE__, __func__, __LINE__).stream()
#define LOG_SYSFATAL \
    tulun::Logger(tulun::LOG_LEVEL::SYSFATAL, __FILE__, __func__, __LINE__).stream()

} // namespace tulun

#endif